谈谈 useCallback
是什么
const memoized = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
- 返回一个 memoized 回调函数,当子组件中接受了父组件传递的函数方法(memoize)时,利用 useCallback 包裹跳过不必要的更新,提高组件性能。
- useCallback(fn, deps) 相当于 useMemo(() => fn, deps)。
示例 1
import React, { useState, useCallback } from 'react';
import Button from './Button';
export default function App() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
const handleClickButton1 = () => {
setCount1(count1 + 1);
};
// 重点!!
const handleClickButton2 = useCallback(() => {
setCount2(count2 + 1);
}, [count2]);
return (
<div>
<div>
<Button onClickButton={handleClickButton1}>Button1</Button>
</div>
<div>
<Button onClickButton={handleClickButton2}>Button2</Button>
</div>
</div>
);
}
// Button.jsx
import React from 'react';
const Button = ({ onClickButton, children }) => {
return (
<>
<button onClick={onClickButton}>{children}</button>
<span>{Math.random()}</span>
</>
);
};
export default React.memo(Button);
点击 Button2 造成 Button1 渲染的原因:
上面的 Button 组件都需要一个 onClickButton 的 props ,尽管组件内部有用 React.memo 来做优化,但是我们声明的 handleClickButton1 是直接定义了一个函数,这也就导致只要是父组件重新渲染就会导致这里声明出一个新的函数,新的函数和旧的函数尽管长的一样,但是依旧是两个不同的对象,React.memo 对比后发现对象 props 改变,就重新渲染了。
const handleClickButton2 = useCallback(() => {
setCount2(count2 + 1);
}, [count2]);
上述代码我们的方法使用 useCallback 包装了一层,并且后面还传入了一个 [count2]
变量,这里 useCallback 就会根据 count2 是否发生变化,从而决定是否返回一个新的函数,函数内部作用域也随之更新。
由于我们的这个方法只依赖了 count2 这个变量,而且 count2 只在点击 Button2 后才会更新 handleClickButton2,所以就导致了我们点击 Button1 不重新渲染 Button2 的内容。
示例 2
父组件
import { useCallback, useState } from "react";
import { Child } from "./child";
export default function App() {
const [count, setCount] = useState(0);
const [name, setName] = useState("小明");
const increment = () => setCount(count + 1);
const onClick = useCallback((name) => {
setName(name);
}, []);
return (
<div>
<button onClick={increment}>点击次数:{count}</button>
<Child name={name} onClick={onClick} />
</div>
);
}
子组件
import React, { memo } from "react";
export const Child = memo((props) => {
const { name, onClick } = props;
console.log("渲染了", name, onClick);
return (
<>
<div>子组件</div>
<button onClick={() => onClick("小红")}>改变 name 值</button>
</>
);
});